package com.sinosoft.sm.system.common.utils;

import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import org.apache.commons.lang3.Validate;

/**
 * 摘要工具类，支持SHA1和MD5算法
 * 公共方法返回ByteSource，可进一步被编码为Hex, Base64或UrlSafeBase64
 * @author xiaoxiong
 *
 */
public class DigestUtil {

	private static final String SHA1 = "SHA-1";	//	SHA1算法
	private static final String MD5 = "MD5";	//	MD5算法
	private static SecureRandom random = new SecureRandom();	//	随机数函数
	
	/**
	 * 对字符串SHA1散列
	 * @param input	输入值
	 * @return	散列后的字符串
	 */
	public static byte[] sha1(byte[] input) {
		return digest(input, SHA1, null, 1);
	}
	
	/**
	 * 对字符串SHA1散列
	 * @param input	输入值
	 * @param salt	盐
	 * @return	散列后的字符串
	 */
	public static byte[] sha1(byte[] input, byte[] salt) {
		return digest(input, SHA1, salt, 1);
	}

	/**
	 * 对字符串SHA1散列
	 * @param input	输入值
	 * @param salt	盐
	 * @param iterations	遍历大小
	 * @return	散列后的字符串
	 */
	public static byte[] sha1(byte[] input, byte[] salt, int iterations) {
		return digest(input, SHA1, salt, iterations);
	}

	/**
	 * 对文件进行MD5散列
	 * @param input	输入值
	 * @return	散列后的文件
	 * @throws IOException	IO异常
	 */
	public static byte[] md5(InputStream input) throws IOException {
		return digest(input, MD5);
	}

	/**
	 * 对文件进行SHA1散列
	 * @param input	输入值
	 * @return	散列后的文件
	 * @throws IOException	IO异常
	 */
	public static byte[] sha1(InputStream input) throws IOException {
		return digest(input, SHA1);
	}
	
	/**
	 * 随机盐生成函数
	 * @param numBytes	byte数组大小
	 * @return	随机盐
	 */
	public static byte[] generateSalt(int numBytes) {
		Validate.isTrue(numBytes > 0, "numBytes argument must be a positive integer (1 or larger)", numBytes);
		byte[] bytes = new byte[numBytes];
		random.nextBytes(bytes);
		return bytes;
	}
	
	/**
	 * 对字符串进行散列，支持MD5与SHA1算法
	 * @param input	输入值
	 * @param algorithm	散列算法
	 * @param salt	盐
	 * @param iterations	遍历大小
	 * @return	散列后的字符串
	 */
	private static byte[] digest(byte[] input, String algorithm, byte[] salt, int iterations) {
		MessageDigest digest = null;
		try {
			digest = MessageDigest.getInstance(algorithm);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		if (salt != null) {
			digest.update(salt);
		}
		byte[] result = digest.digest(input);
		for (int i = 1; i < iterations; i++) {
			digest.reset();
			result = digest.digest(result);
		}
		return result;
	}
	
	/**
	 * 对文件进行散列，支持MD5与SHA1算法
	 * @param input	输入值
	 * @param algorithm	算法
	 * @return	散列后的文件
	 * @throws IOException	IO异常
	 */
	private static byte[] digest(InputStream input, String algorithm) throws IOException{
		MessageDigest messageDigest = null;
		try {
			messageDigest = MessageDigest.getInstance(algorithm);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		int bufferLength = 8 * 1024;
		byte[] buffer = new byte[bufferLength];
		int read = input.read(buffer, 0, bufferLength);

		while (read > -1) {
			messageDigest.update(buffer, 0, read);
			read = input.read(buffer, 0, bufferLength);
		}
		return messageDigest.digest();
	}
}
